package com.apobates.jforum2.troll.actionlog.biz.impl.dao;

import com.apobates.jforum2.troll.regular.ForumActionEnum;
import com.apobates.jforum2.troll.actionlog.biz.dao.TopicActionCollectionDao;
import com.apobates.jforum2.troll.actionlog.entity.TopicActionCollection;
import com.apobates.jforum2.troll.utils.core.DateTimeUtils;
import com.apobates.jforum2.troll.utils.persistence.Page;
import com.apobates.jforum2.troll.utils.persistence.Pageable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Stream;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 *
 * @author xiaofanku
 * @since 20200514
 */
@Repository
public class TopicActionCollectionDaoImpl implements TopicActionCollectionDao{
    @PersistenceContext
    private EntityManager entityManager;
    private final static Logger logger = LoggerFactory.getLogger(TopicActionCollectionDaoImpl.class);
    
    @Override
    public Page<TopicActionCollection> findByMember(long memberId, Pageable pageable) {
        final long total = findByMemberCount(memberId);
        if (total == 0) {
            return emptyResult();
        }
        TypedQuery<TopicActionCollection> query = entityManager.createQuery("SELECT tac FROM TopicActionCollection tac WHERE tac.memberId = ?1 ORDER BY tac.entryDateTime DESC", TopicActionCollection.class).setParameter(1, memberId);
        query.setFirstResult(pageable.getOffset());
        query.setMaxResults(pageable.getPageSize());
        
        final Stream<TopicActionCollection> result = query.getResultStream();
        return new Page<TopicActionCollection>() {
            @Override
            public long getTotalElements() {
                return total;
            }
            
            @Override
            public Stream<TopicActionCollection> getResult() {
                return result;
            }
        };
    }
    
    @Override
    public long findByMemberCount(long memberId) {
        try {
            return entityManager.createQuery("SELECT COUNT(tac) FROM TopicActionCollection tac WHERE tac.memberId = ?1", Long.class)
                    .setParameter(1, memberId)
                    .getSingleResult();
        } catch (Exception e) {
            if (logger.isDebugEnabled()) {
                logger.debug("[findByMemberCount][TopicActionCollectionDao]", e);
            }
        }
        return 0L;
    }
    
    @Override
    public Page<TopicActionCollection> findByTopic(long topicId, Pageable pageable) {
        final long total = countByTopicAction(topicId);
        if (total == 0) {
            return emptyResult();
        }
        TypedQuery<TopicActionCollection> query = entityManager.createQuery("SELECT tac FROM TopicActionCollection tac WHERE tac.topicId = ?1 ORDER BY tac.entryDateTime DESC", TopicActionCollection.class).setParameter(1, topicId);
        query.setFirstResult(pageable.getOffset());
        query.setMaxResults(pageable.getPageSize());
        
        final Stream<TopicActionCollection> result = query.getResultStream();
        return new Page<TopicActionCollection>() {
            @Override
            public long getTotalElements() {
                return total;
            }
            
            @Override
            public Stream<TopicActionCollection> getResult() {
                return result;
            }
        };
    }
    private long countByTopicAction(long topicId) {
        try {
            return entityManager.createQuery("SELECT COUNT(tac) FROM TopicActionCollection tac WHERE tac.topicId = ?1", Long.class)
                    .setParameter(1, topicId)
                    .getSingleResult();
        } catch (Exception e) {
            if (logger.isDebugEnabled()) {
                logger.debug("[countByTopicAction][TopicActionCollectionDao]", e);
            }
        }
        return 0L;
    }
    @Override
    public Page<TopicActionCollection> findAllByAction(ForumActionEnum action, Pageable pageable) {
        final long total = findAllByActionCount(action);
        if (total == 0) {
            return emptyResult();
        }
        TypedQuery<TopicActionCollection> query = entityManager.createQuery("SELECT tac FROM TopicActionCollection tac WHERE tac.action = ?1 ORDER BY tac.entryDateTime DESC", TopicActionCollection.class).setParameter(1, action);
        query.setFirstResult(pageable.getOffset());
        query.setMaxResults(pageable.getPageSize());
        
        final Stream<TopicActionCollection> result = query.getResultStream();
        return new Page<TopicActionCollection>() {
            @Override
            public long getTotalElements() {
                return total;
            }
            
            @Override
            public Stream<TopicActionCollection> getResult() {
                return result;
            }
        };
    }
    private long findAllByActionCount(ForumActionEnum action) {
        try {
            return entityManager.createQuery("SELECT COUNT(tac) FROM TopicActionCollection tac WHERE tac.action = ?1", Long.class)
                    .setParameter(1, action)
                    .getSingleResult();
        } catch (Exception e) {
            if (logger.isDebugEnabled()) {
                logger.debug("[findAllByActionCount][TopicActionCollectionDao]", e);
            }
        }
        return 0L;
    }
    @Override
    public Page<TopicActionCollection> findAllByActionAndMember(ForumActionEnum action, long memberId, Pageable pageable) {
        final long total = findAllByActionAndMemberCount(action, memberId);
        if (total == 0) {
            return emptyResult();
        }
        TypedQuery<TopicActionCollection> query = entityManager.createQuery("SELECT tac FROM TopicActionCollection tac WHERE tac.action = ?1 AND tac.memberId = ?2 ORDER BY tac.entryDateTime DESC", TopicActionCollection.class)
                .setParameter(1, action)
                .setParameter(2, memberId);
        query.setFirstResult(pageable.getOffset());
        query.setMaxResults(pageable.getPageSize());
        
        final Stream<TopicActionCollection> result = query.getResultStream();
        return new Page<TopicActionCollection>() {
            @Override
            public long getTotalElements() {
                return total;
            }
            
            @Override
            public Stream<TopicActionCollection> getResult() {
                return result;
            }
        };
    }
    
    @Override
    public long findAllByActionAndMemberCount(ForumActionEnum action, long memberId) {
        try {
            return entityManager
                    .createQuery("SELECT COUNT(tac) FROM TopicActionCollection tac WHERE tac.action = ?1 AND tac.memberId = ?2", Long.class)
                    .setParameter(1, action)
                    .setParameter(2, memberId)
                    .getSingleResult();
        } catch (Exception e) {
            if (logger.isDebugEnabled()) {
                logger.debug("[findAllByActionAndMemberCount][TopicActionCollectionDao]", e);
            }
        }
        return 0L;
    }
    
    @Override
    public Page<TopicActionCollection> findAllByActionAndMember(List<ForumActionEnum> actions, long memberId, Pageable pageable) {
        final long total = countAllByActionAndMember(actions, memberId);
        if (total == 0) {
            return emptyResult();
        }
        TypedQuery<TopicActionCollection> query = entityManager.createQuery("SELECT tac FROM TopicActionCollection tac WHERE tac.memberId = ?1 AND tac.action IN ?2 ORDER BY tac.entryDateTime DESC", TopicActionCollection.class)
                .setParameter(1, memberId)
                .setParameter(2, actions);
        query.setFirstResult(pageable.getOffset());
        query.setMaxResults(pageable.getPageSize());
        
        final Stream<TopicActionCollection> result = query.getResultStream();
        return new Page<TopicActionCollection>() {
            @Override
            public long getTotalElements() {
                return total;
            }
            
            @Override
            public Stream<TopicActionCollection> getResult() {
                return result;
            }
        };
    }
    private long countAllByActionAndMember(List<ForumActionEnum> actions, long memberId) {
        if (actions == null || actions.isEmpty()) {
            return 0L;
        }
        try {
            return entityManager.createQuery("SELECT COUNT(tac) FROM TopicActionCollection tac WHERE tac.memberId = ?1 AND tac.action IN ?2", Long.class)
                    .setParameter(1, memberId)
                    .setParameter(2, actions)
                    .getSingleResult();
        } catch (Exception e) {
            if (logger.isDebugEnabled()) {
                logger.debug("[countAllByActionAndMember][TopicActionCollectionDao]", e);
            }
        }
        return 0L;
    }
    @Override
    public Page<TopicActionCollection> findAll(LocalDateTime start, LocalDateTime finish, Pageable pageable) {
        final long total = findAllCount(start, finish);
        if (total == 0) {
            return emptyResult();
        }
        TypedQuery<TopicActionCollection> query = entityManager.createQuery("SELECT tac FROM TopicActionCollection tac WHERE tac.entryDateTime BETWEEN ?1 AND ?2 ORDER BY tac.entryDateTime DESC", TopicActionCollection.class)
                .setParameter(1, start)
                .setParameter(2, finish);
        query.setFirstResult(pageable.getOffset());
        query.setMaxResults(pageable.getPageSize());
        
        final Stream<TopicActionCollection> result = query.getResultStream();
        return new Page<TopicActionCollection>() {
            @Override
            public long getTotalElements() {
                return total;
            }
            
            @Override
            public Stream<TopicActionCollection> getResult() {
                return result;
            }
        };
    }
    private long findAllCount(LocalDateTime start, LocalDateTime finish) {
        try {
            return entityManager.createQuery("SELECT COUNT(tac) FROM TopicActionCollection tac WHERE tac.entryDateTime BETWEEN ?1 AND ?2", Long.class)
                    .setParameter(1, start)
                    .setParameter(2, finish)
                    .getSingleResult();
        } catch (Exception e) {
            if (logger.isDebugEnabled()) {
                logger.debug("[findAllCount][TopicActionCollectionDao]", e);
            }
        }
        return 0L;
    }
    @Override
    public Stream<TopicActionCollection> findRecentByMember(long memberId, int size) {
        return entityManager.createQuery("SELECT tac FROM TopicActionCollection tac WHERE tac.memberId = ?1 ORDER BY tac.entryDateTime DESC", TopicActionCollection.class)
                .setParameter(1, memberId)
                .setMaxResults(size)
                .getResultStream();
    }
    
    @Override
    public Stream<TopicActionCollection> findAllByMemberAction(long memberId, long topicId, ForumActionEnum action) {
        return entityManager.createQuery("SELECT tac FROM TopicActionCollection tac WHERE tac.action = ?1 AND tac.memberId = ?2 AND tac.topicId = ?3", TopicActionCollection.class)
                .setParameter(1, action)
                .setParameter(2, memberId)
                .setParameter(3, topicId)
                .getResultStream();
    }
    
    @Override
    public Stream<ForumActionEnum> findAllAction() {
        return entityManager.createQuery("SELECT DISTINCT tac.action FROM TopicActionCollection tac", ForumActionEnum.class).getResultStream();
    }
    
    @Override
    public Optional<TopicActionCollection> findOneFavoriteRecord(long memberId, long topicId) {
        try {
            TopicActionCollection tac = entityManager.createQuery("SELECT tac FROM TopicActionCollection tac WHERE tac.action = ?1 AND tac.memberId = ?2 AND tac.topicId = ?3", TopicActionCollection.class)
                    .setParameter(1, ForumActionEnum.TOPIC_FAVORITE)
                    .setParameter(2, memberId)
                    .setParameter(3, topicId)
                    .getSingleResult();
            return Optional.ofNullable(tac);
        } catch (javax.persistence.NoResultException e) {
            if(logger.isDebugEnabled()){
                logger.debug(e.getMessage(), e);
            }
            return Optional.empty();
        }
    }
    
    @Override
    public Optional<TopicActionCollection> findOneLikeRecord(long memberId, long topicId) {
        try {
            TopicActionCollection tac = entityManager.createQuery("SELECT tac FROM TopicActionCollection tac WHERE tac.action = ?1 AND tac.memberId = ?2 AND tac.topicId = ?3", TopicActionCollection.class)
                    .setParameter(1, ForumActionEnum.TOPIC_LIKED)
                    .setParameter(2, memberId)
                    .setParameter(3, topicId)
                    .getSingleResult();
            return Optional.ofNullable(tac);
        } catch (javax.persistence.NoResultException e) {
            if(logger.isDebugEnabled()){
                logger.debug(e.getMessage(), e);
            }
            return Optional.empty();
        }
    }
    
    @Override
    public Map<ForumActionEnum, Long> groupMemberAction(long memberId, Collection<ForumActionEnum> allowActions) {
        if (allowActions == null || allowActions.isEmpty()) {
            return Collections.emptyMap();
        }
        @SuppressWarnings("unchecked")
        List<Object[]> result = entityManager.createQuery("SELECT tac.action, COUNT(tac) FROM TopicActionCollection tac WHERE tac.memberId = ?1 AND tac.action IN ?2 GROUP BY tac.action")
                .setParameter(1, memberId)
                .setParameter(2, allowActions)
                .getResultList();
        return groupMemberActionResultMapping(result);
    }
    
    @Override
    public Map<ForumActionEnum, Long> groupMemberAction(long memberId) {
        @SuppressWarnings("unchecked")
        List<Object[]> result = entityManager.createQuery("SELECT tac.action, COUNT(tac) FROM TopicActionCollection tac WHERE tac.rodeMember = ?1 GROUP BY tac.action")
                .setParameter(1, memberId)
                .getResultList();
        return groupMemberActionResultMapping(result);
    }
    
    @Override
    public Map<ForumActionEnum, Long> groupAction(List<ForumActionEnum> allowActions) {
        if (allowActions == null || allowActions.isEmpty()) {
            return Collections.emptyMap();
        }
        @SuppressWarnings("unchecked")
        List<Object[]> result = entityManager.createQuery("SELECT tac.action, COUNT(tac) FROM TopicActionCollection tac WHERE tac.action IN ?1 GROUP BY tac.action").setParameter(1, allowActions).getResultList();
        return groupMemberActionResultMapping(result);
    }
    
    @Override
    public Map<Long, Map<ForumActionEnum, Long>> groupMemberAction(List<Long> memberIdList, Collection<ForumActionEnum> allowActions) {
        if (allowActions == null || allowActions.isEmpty()) {
            return Collections.emptyMap();
        }
        Map<Long, Map<ForumActionEnum, Long>> data = new HashMap<>();
        for (Long memberId : memberIdList) {
            Map<ForumActionEnum, Long> stats = groupMemberAction(memberId, allowActions);
            if (!stats.isEmpty()) {
                data.put(memberId, stats);
            }
        }
        return data;
    }
    
    @Override
    public Map<Long, Map<String, Long>> groupMemberAction(ForumActionEnum action, int size) {
        if (action == null) {
            return Collections.emptyMap();
        }
        @SuppressWarnings("unchecked")
        List<Object[]> result = entityManager.createQuery("SELECT tac.memberId, tac.memberNickname, COUNT(tac) FROM TopicActionCollection tac WHERE tac.action = ?1 GROUP BY tac.memberId ORDER BY COUNT(tac) DESC")
                .setParameter(1, action)
                .setMaxResults(size)
                .getResultList();
        Map<Long, Map<String, Long>> data = new HashMap<>();
        for (Object[] columns : result) {
            Map<String, Long> iM = new HashMap<>();
            Long tmpV = 0L;
            try {
                tmpV = ((BigDecimal) columns[2]).longValue();
            } catch (ClassCastException e) {
                tmpV = (Long) columns[2];
            }
            Long tmpK = 0L;
            try {
                tmpK = ((BigDecimal) columns[0]).longValue();
            } catch (ClassCastException e) {
                tmpK = (Long) columns[0];
            }
            iM.put(columns[1].toString(), tmpV);
            data.put(tmpK, iM);
        }
        return data;
    }
    
    @Override
    public long countMemberAction(long memberId, long topicId, ForumActionEnum action) {
        logger.error("[TopicActionDao][count]args: topic id=" + topicId + ", member id=" + memberId + ", action=" + action.name());
        try {
            return entityManager.createQuery("SELECT COUNT(tac) FROM TopicActionCollection tac WHERE tac.action = ?1 AND tac.memberId = ?2 AND tac.topicId = ?3", Long.class)
                    .setParameter(1, action)
                    .setParameter(2, memberId)
                    .setParameter(3, topicId)
                    .getSingleResult();
        } catch (Exception e) {
            if (logger.isDebugEnabled()) {
                logger.debug("[countMemberAction][TopicActionCollectionDao]", e);
            }
        }
        return 0L;
    }
    
    @Override
    public Optional<Boolean> isLiked(long topicId, long memberId) {
        logger.error("[TopicActionDao][like]args: topic id=" + topicId + ", member id=" + memberId);
        if (memberId > 0 && topicId > 0) {
            long data = countMemberAction(memberId, topicId, ForumActionEnum.TOPIC_LIKED);
            logger.error("[TopicActionDao][like]result: size=" + data);
            if (data > 0) {
                logger.error("[TopicActionDao][like]result: is exist");
                return Optional.empty();//failure("已经点过赞了");
            }
            return Optional.of(true);
        }
        return Optional.empty(); //("非法的参数");
    }
    
    @Override
    public Optional<Boolean> isFavorited(long topicId, long memberId) {
        logger.error("[TopicActionDao][favorite]args: topic id=" + topicId + ", member id=" + memberId);
        if (memberId > 0 && topicId > 0) {
            long data = countMemberAction(memberId, topicId, ForumActionEnum.TOPIC_FAVORITE);
            logger.error("[TopicActionDao][favorite]favorite: size=" + data);
            if (data > 0) {
                logger.error("[TopicActionDao][favorite]result: is exist");
                return Optional.empty();//failure("收藏记录已经存在");
            }
            return Optional.of(true);
        }
        return Optional.empty();//("非法的参数");
    }
    
    @Override
    public Page<TopicActionCollection> findAll(Pageable pageable) {
        final long total = count();
        if (total == 0) {
            return emptyResult();
        }
        TypedQuery<TopicActionCollection> query = entityManager.createQuery("SELECT tac FROM TopicActionCollection tac ORDER BY tac.entryDateTime DESC", TopicActionCollection.class);
        query.setFirstResult(pageable.getOffset());
        query.setMaxResults(pageable.getPageSize());
        
        final Stream<TopicActionCollection> result = query.getResultStream();
        return new Page<TopicActionCollection>() {
            @Override
            public long getTotalElements() {
                return total;
            }
            
            @Override
            public Stream<TopicActionCollection> getResult() {
                return result;
            }
        };
    }
    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void save(TopicActionCollection entity) {
        logger.error("[MDA][DAO]mda topic action: "+entity.getAction().getTitle()+", operator: "+entity.getMemberNickname());
        List<ForumActionEnum> oneRecorded = Arrays.asList(
                ForumActionEnum.COMMON_VISIT,
                ForumActionEnum.TOPIC_BROWSE,
                ForumActionEnum.COMMON_CASUAL,
                ForumActionEnum.BOARD_HOME,
                ForumActionEnum.BOARD_BROWSE,
                ForumActionEnum.BOARD_GROUP_BROWSE,
                ForumActionEnum.MESSAGE_BROWSE);
        // 一个会员一天浏览只记录一次
        if (oneRecorded.contains(entity.getAction()) && entity.getMemberId() > 0) {
            entityManager.createQuery("DELETE FROM TopicActionCollection tc WHERE tc.topicId = ?1 AND tc.memberId = ?2 AND tc.action = ?3 AND tc.entryDateTime BETWEEN ?4 AND ?5")
                    .setParameter(1, entity.getTopicId())
                    .setParameter(2, entity.getMemberId())
                    .setParameter(3, entity.getAction())
                    .setParameter(4, DateTimeUtils.getTodayEarlyMorning())
                    .setParameter(5, DateTimeUtils.getTodayMidnight())
                    .executeUpdate();
        }
        entityManager.persist(entity);
    }
    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public int batchSave(Collection<TopicActionCollection> entities) {
        if(null == entities || entities.isEmpty()){
            return 0;
        }
        return entities.stream().reduce(0, (index, tac)->{
            try{
                save(tac);
                return index+1;
            }catch(Exception e){
                return index;
            }
        }, Integer::sum);
    }
    
    @Override
    public Optional<TopicActionCollection> findOne(Long primaryKey) {
        return Optional.ofNullable(entityManager.find(TopicActionCollection.class, primaryKey));
    }
    
    @Override
    public Optional<Boolean> edit(TopicActionCollection updateEntity) {
        return Optional.empty();
    }
    
    @Override
    public Stream<TopicActionCollection> findAll() {
        return Stream.empty();
    }
    
    @Override
    public long count() {
        try {
            return entityManager.createQuery("SELECT COUNT(tac) FROM TopicActionCollection tac", Long.class).getSingleResult();
        } catch (Exception e) {
            if (logger.isDebugEnabled()) {
                logger.debug("[count][TopicActionCollectionDao]", e);
            }
        }
        return 0L;
    }
    private Map<ForumActionEnum, Long> groupMemberActionResultMapping(List<Object[]> result) {
        Map<ForumActionEnum, Long> data = new HashMap<>();
        if (result == null || result.isEmpty()) {
            return data;
        }
        for (Object[] objArr : result) {
            ForumActionEnum action = null;
            if (objArr[0] instanceof ForumActionEnum) {
                action = (ForumActionEnum) objArr[0];
            }
            if (action == null) {
                continue;
            }
            Long size = 0L;
            try {
                size = ((BigDecimal) objArr[1]).longValue();
            } catch (ClassCastException e) {
                size = (Long) objArr[1];
            }
            data.put(action, size);
        }
        return data;
    }

    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void detele(long topicId, long memberId, ForumActionEnum action) {
        entityManager
                .createQuery("DELETE FROM TopicActionCollection tac WHERE tac.topicId = ?1 AND tac.memberId = ?2 AND tac.action = ?3")
                .setParameter(1, topicId)
                .setParameter(2, memberId)
                .setParameter(3, action)
                .executeUpdate();
    }
    
}